﻿using Colorful.Caching;
using Colorful.Furniture.CommonDto;
using Colorful.Furniture.SysManagement;
using FreeSql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.MultiTenancy;
using Volo.Abp.SimpleStateChecking;

namespace Colorful.Furniture.PermissionManagement
{
    /// <summary>
    ///  权限服务
    /// </summary>
    [Authorize]
    public class PermissionAppService : ApplicationService
    {
        private readonly IBaseRepository<SysApi> _sysApiRepository;
        private readonly IBaseRepository<SysResource> _sysResourceRepository;
        private readonly IBaseRepository<SysPermission> _sysPermissionRepository;
        private readonly IBaseRepository<SysPermissionApi> _sysPermissionApiRepository;
        private readonly IBaseRepository<SysDictionary> _sysDictionaryRepository;
        private readonly IRedisCache _redisCache;
        private readonly IFreeSql _freeSql;
        private readonly ILogger<PermissionAppService> _logger;
        private readonly IConfiguration _configuration;
        protected IPermissionDefinitionManager PermissionDefinitionManager { get; }
        protected ISimpleStateCheckerManager<PermissionDefinition> SimpleStateCheckerManager { get; }

        /// <summary>
        /// 权限服务
        /// </summary>
        public PermissionAppService(
            IBaseRepository<SysApi> sysApiRepository,
            IBaseRepository<SysResource> sysResourceRepository,
            IBaseRepository<SysPermission> sysPermissionRepository,
            IFreeSql freeSql,
            IBaseRepository<SysPermissionApi> sysPermissionApiRepository,
            IBaseRepository<SysDictionary> sysDictionaryRepository,
            IPermissionDefinitionManager permissionDefinitionManager,
            ISimpleStateCheckerManager<PermissionDefinition> simpleStateCheckerManager,
            IRedisCache redisCache,
            ILogger<PermissionAppService> logger,
            IConfiguration configuration)
        {
            _sysApiRepository = sysApiRepository;
            _sysResourceRepository = sysResourceRepository;
            _sysPermissionRepository = sysPermissionRepository;
            _freeSql = freeSql;
            _sysPermissionApiRepository = sysPermissionApiRepository;
            _sysDictionaryRepository = sysDictionaryRepository;
            PermissionDefinitionManager = permissionDefinitionManager;
            SimpleStateCheckerManager = simpleStateCheckerManager;
            _redisCache = redisCache;
            _logger = logger;
            _configuration = configuration;
        }

        /// <summary>
        /// 同步前端资源数据
        /// </summary>
        /// <returns></returns>
        public async Task SyncResourceDataAsync(List<SyncResourceInputDto> input)
        {
            //获取所有的资源列表
            var resourceList = await _sysResourceRepository
                .Where(x => !x.IsDeleted)
                .ToListAsync();

            var resourceListInput = ObjectMapper.Map<List<SyncResourceInputDto>, List<SysResource>>(input);

            var inputNameList = new List<string>();

            await RecursionSyncResourceAsync(resourceListInput, null, resourceList, inputNameList);

            //删除的资源
            var deleteNameList = resourceList.Select(x => x.Name).Except(inputNameList)
                .ToList();

            if (deleteNameList.Any())
                await _sysResourceRepository.DeleteAsync(x => deleteNameList.Contains(x.Name));
        }

        /// <summary>
        ///  同步所有授权的action
        /// </summary>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task SyncApiAsync()
        {
            var sysApiInputDtoList = new List<SysApiInputDto>();

            //获取当前多租户的
            var multiTenancySide = CurrentTenant.GetMultiTenancySide();

            //MVC的控制器分组Area
            foreach (var areaPermissionGroup in PermissionDefinitionManager.GetGroups())
            {
                //MVC里面的Controller
                var permissionList = areaPermissionGroup.GetPermissionsWithChildren()
                    .Where(x => x.IsEnabled)
                    //.Where(x => !x.Providers.Any() || x.Providers.Contains(providerName))
                    .Where(x => x.MultiTenancySide.HasFlag(multiTenancySide));

                foreach (var controllerPermission in permissionList)
                {
                    if (await SimpleStateCheckerManager.IsEnabledAsync(controllerPermission))
                    {
                        //MVC里面的Action
                        foreach (var actionPermission in controllerPermission.Children)
                        {
                            if (await SimpleStateCheckerManager.IsEnabledAsync(actionPermission))
                            {
                                sysApiInputDtoList.Add(new SysApiInputDto
                                {
                                    AreaCode = areaPermissionGroup.Name,
                                    AreaTitle = areaPermissionGroup.DisplayName.Localize(StringLocalizerFactory),

                                    ControllerTitle = controllerPermission.DisplayName.Localize(StringLocalizerFactory),
                                    ControllerCode = controllerPermission.Name,

                                    ActionCode = actionPermission.Name,
                                    ActionTitle = actionPermission.DisplayName.Localize(StringLocalizerFactory),
                                    AuthorizationType = 2,
                                    AuthorizationTypeName = "角色权限",

                                    Route = actionPermission.Name,

                                    Description = string.Empty,

                                    ModifyTime = DateTime.Now
                                });
                            }
                            else
                            {
                                _logger.LogInformation($"权限[{actionPermission.DisplayName}]未启用，跳过");
                            }
                        }
                    }
                    else
                    {
                        _logger.LogInformation($"权限[{controllerPermission.DisplayName}]未启用，跳过");
                    }
                }
            }

            //判断接口中是否有重复的actionTitle和接口路径
            var controllerActionGroupMultiple = sysApiInputDtoList
                .GroupBy(x => new { x.ControllerTitle, x.ActionTitle, x.AreaTitle })
                .Where(x => x.Count() > 1)
                .Select(x => $"权限点 {x.Key.AreaTitle}-{x.Key.ControllerTitle}-{x.Key.ActionTitle} 重复")
                .ToList();

            if (controllerActionGroupMultiple.Any())
                throw new UserFriendlyException($"{string.Join(";", controllerActionGroupMultiple)}");

            //从缓存中获取api接口数据集合
            var sysApiOutputList = _redisCache.Get<List<SysApiInputDto>>(CachePrefixConst.SysApi);

            //如果缓存没有数据则查询数据库
            if (sysApiOutputList == null || !sysApiOutputList.Any())
            {
                sysApiOutputList = _freeSql.Select<SysApi>()
                    .ToList<SysApiInputDto>();
            }

            var sysApiOutputRouteList = sysApiOutputList
                .Select(x => x.Route)
                .ToList();

            var sysApiInputRouteList = sysApiInputDtoList
                .Select(x => x.Route)
                .ToList();

            var sysApiInsertRouteList = sysApiInputRouteList.Except(sysApiOutputRouteList).ToList();

            var sysApiDeleteRouteList = sysApiOutputRouteList.Except(sysApiInputRouteList).ToList();

            var trans = _freeSql.CreateUnitOfWork().GetOrBeginTransaction();

            //新增
            var insetSysApiList =
                ObjectMapper.Map<List<SysApiInputDto>, List<SysApi>>(sysApiInputDtoList.Where(x => sysApiInsertRouteList.Contains(x.Route)).ToList());

            if (insetSysApiList.Any())
            {
                await _freeSql.Insert(insetSysApiList)
                    .WithTransaction(trans)
                    .ExecuteAffrowsAsync();
            }

            //修改
            foreach (var sysApiInputDto in sysApiInputDtoList)
            {
                await _freeSql.Update<SysApi>()
                    .WithTransaction(trans)
                    .Where(x => x.Route == sysApiInputDto.Route && !x.IsNotSync)
                    .SetDto(sysApiInputDto)
                    .ExecuteAffrowsAsync();
            }

            //物理删除
            if (sysApiDeleteRouteList.Any())
            {
                await _freeSql.Delete<SysApi>()
                    .Where(x => sysApiDeleteRouteList.Contains(x.Route))
                    .WithTransaction(trans)
                    .ExecuteAffrowsAsync();
            }

            await trans.CommitAsync();

            //异步写入缓存
            _ = _redisCache.SetAsync(CachePrefixConst.SysApi, sysApiInputDtoList);
        }

        /// <summary>
        ///  获取权限定义的dto类
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, SwaggerSchemaObject>> GetDefinePermissionDtoAsync()
        {
            //定义权限dto类集合
            var definePermissionDtoList = typeof(FurnitureApplicationContractsModule).Assembly
                 .GetTypes()
                 .Where(x => x.GetInterfaces().Contains(typeof(IDefinePermissionDto)) && !x.IsAbstract && x.IsClass)
                 .ToList();

            var appDomain = _configuration.GetSection("App:Domain").Value;

            if (string.IsNullOrWhiteSpace(appDomain))
                throw new UserFriendlyException("请在配置文件中配置App:Domain节点");

            var list = new Dictionary<string, SwaggerSchemaObject>();

            using var client = new HttpClient();

            var result = await client.GetAsync($"{appDomain.RemovePostFix("/")}/swagger/v1/swagger.json");

            if (result.StatusCode == HttpStatusCode.OK)
            {
                var swaggerJsonResponseString = await result.Content.ReadAsStringAsync();

                var swaggerApiDto = JsonConvert.DeserializeObject<SwaggerApiDto>(swaggerJsonResponseString);

                if (swaggerApiDto == null)
                    throw new UserFriendlyException("未能成功解析swagger.json文件");

                var findPermissionDtoDictionary = definePermissionDtoList
                    .Select(type => swaggerApiDto.Components.Schemas.FirstOrDefault(x => x.Key == type.FullName))
                    .ToList();

                foreach (var swaggerApiSchema in findPermissionDtoDictionary)
                {
                    list.Add(swaggerApiSchema.Key, swaggerApiSchema.Value);
                }
            }

            return list;
        }

        /// <summary>
        ///  获取定义的权限列表数据
        /// </summary>
        /// <returns></returns>
        [RemoteService(false)]
        public virtual async Task<GetPermissionListResultDto> GetAllListAsync()
        {
            var result = new GetPermissionListResultDto
            {
                EntityDisplayName = "权限管理",
                Groups = new List<PermissionGroupDto>()
            };

            var multiTenancySide = CurrentTenant.GetMultiTenancySide();

            foreach (var group in PermissionDefinitionManager.GetGroups())
            {
                var groupDto = new PermissionGroupDto
                {
                    Name = group.Name,
                    DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
                    Permissions = new List<PermissionGrantInfoDto>()
                };

                var neededCheckPermissions = new List<PermissionDefinition>();

                foreach (var permission in group.GetPermissionsWithChildren()
                                                .Where(x => x.IsEnabled)
                                                //.Where(x => !x.Providers.Any() || x.Providers.Contains(providerName))
                                                .Where(x => x.MultiTenancySide.HasFlag(multiTenancySide)))
                {
                    if (await SimpleStateCheckerManager.IsEnabledAsync(permission))
                    {
                        neededCheckPermissions.Add(permission);
                    }
                }

                if (!neededCheckPermissions.Any())
                {
                    continue;
                }

                //授权信息
                var grantInfoDtos = neededCheckPermissions.Select(x => new PermissionGrantInfoDto
                {
                    Name = x.Name,
                    DisplayName = x.DisplayName.Localize(StringLocalizerFactory),
                    ParentName = x.Parent?.Name,
                    AllowedProviders = x.Providers
                }).ToList();

                foreach (var grantInfoDto in grantInfoDtos.Select(grantInfo => grantInfoDtos.First(x => x.Name == grantInfo.Name)))
                {
                    groupDto.Permissions.Add(grantInfoDto);
                }

                if (groupDto.Permissions.Any())
                {
                    result.Groups.Add(groupDto);
                }
            }

            return result;
        }

        /// <summary>
        ///  递归处理下面的数据
        /// </summary>
        /// <param name="inputResourceList">传入的资源列表</param>
        /// <param name="parentId">父级编码</param>
        /// <param name="allResourceList">从数据库中查询现有的资源列表</param>
        /// <param name="inputAllResourceName">传入的所有资源名字集合</param>
        /// <returns></returns>
        private async Task RecursionSyncResourceAsync(List<SysResource> inputResourceList, Guid? parentId, List<SysResource> allResourceList, List<string> inputAllResourceName)
        {
            if (inputResourceList != null)
            {
                var index = 0;
                foreach (var resource in inputResourceList)
                {
                    inputAllResourceName.Add(resource.Name);

                    index++;

                    var resourceWithDb = allResourceList.FirstOrDefault(x => x.Name == resource.Name);

                    if (resourceWithDb != null)
                    {
                        var resourceTitle = resource.Title;
                        resource.Title = string.Empty;

                        resource.Id = resourceWithDb.Id;

                        _sysResourceRepository.Attach(resource);

                        resource.ParentId = parentId;
                        resource.Title = resourceTitle;
                        resource.Sort = resourceWithDb.Sort;

                        await _sysResourceRepository.UpdateAsync(resource);
                    }
                    else
                    {
                        resource.Id = GuidGenerator.Create();
                        resource.ParentId = parentId;
                        resource.Sort = index;

                        await _sysResourceRepository.InsertAsync(resource);
                    }

                    await RecursionSyncResourceAsync(resource.Children, resource.Id, allResourceList, inputAllResourceName);
                }
            }
        }

        /// <summary>
        ///  获取资源树形列表
        /// </summary>
        /// <returns></returns>
        public async Task<Tuple<List<ResourceTableTreeOutputDto>, List<ResourcePermissionOutputDto>>> GetResourceTreeListAsync()
        {
            //获取资源的操作列表
            var resourcePermissionList = await _sysDictionaryRepository
                .Where(x => x.Parent.Code == "ResourceOperation")
                .OrderBy(x => x.Sort)
                .ToListAsync(x => new ResourcePermissionOutputDto
                {
                    OperationName = x.Code,
                    OperationTitle = x.Title
                });

            //所有所有的资源列表数据
            var sysResourceList = await _sysResourceRepository
                .Where(x => !x.IsDeleted)
                .IncludeMany(x => x.SysPermissions, then => then.IncludeMany(y => y.SysPermissionApis))
                .ToListAsync();

            //递归获取资源列表数据集合权限操作集合
            var treeOutputDto = RecursionGetResourceTreeList(sysResourceList, resourcePermissionList);

            //资源已有的权限列表
            var permissionOutputList = sysResourceList
                .SelectMany(x => x.SysPermissions.Select(y => new ResourcePermissionOutputDto
                {
                    Id = y.Id,
                    IsChecked = true,
                    OperationTitle = y.OperationTitle,
                    OperationName = y.OperationName,
                    OperationSort = y.OperationSort,
                    ResourceId = y.ResourceId
                }))
                .ToList();

            return Tuple.Create(treeOutputDto, permissionOutputList);
        }

        /// <summary>
        ///   递归获取菜单下级和相应的权限操作列表
        /// </summary>
        /// <returns></returns>
        public static List<ResourceTableTreeOutputDto> RecursionGetResourceTreeList(
            List<SysResource> resourceList,
            List<ResourcePermissionOutputDto> resourcePermissionList,
            Guid? parentId = null)
        {
            return resourceList
                .OrderBy(x => x.Sort)
                .Where(x => x.ParentId == parentId)
                .Select(x => new ResourceTableTreeOutputDto
                {
                    Id = x.Id,
                    Label = $"{x.Title}{(x.IsHide == true ? "[隐藏]" : "")}",
                    Name = x.Name,
                    IsCheckAll = resourcePermissionList.Count != 0 && resourcePermissionList.Count == x.SysPermissions.Count,
                    IsIndeterminate = x.SysPermissions.Count > 0 && resourcePermissionList.Count > x.SysPermissions.Count,
                    ParentId = x.ParentId,
                    IsHasPermission = x.SysPermissions.Count > 0,

                    Permissions = resourcePermissionList.Select(a =>
                    {
                        //获取菜单的指定操作权限
                        var permission = x.SysPermissions.FirstOrDefault(ss =>
                            ss.OperationName == a.OperationName && ss.ResourceId == x.Id);

                        var b = new ResourcePermissionOutputDto
                        {
                            //操作选中
                            OperationName = a.OperationName,
                            OperationSort = a.OperationSort,
                            OperationTitle = a.OperationTitle,
                            IsChecked = permission != null,

                            //接口权限数量
                            ApiCount = permission?.SysPermissionApis?.Count,

                            //菜单信息
                            ResourceId = x.Id,

                            //权限编码
                            Id = permission?.Id,
                        };

                        return b;
                    }).ToList(),

                    Children = RecursionGetResourceTreeList(resourceList, resourcePermissionList, x.Id)
                }).ToList();
        }

        /// <summary>
        ///  获取接口树形列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<TreeOutputDto<string>>> GetApiTreeListAsync()
        {
            var sysApiList = await _sysApiRepository
                .Where(x => !x.IsDeleted)
                .ToListAsync();

            var treeOutputDto = sysApiList.GroupBy(x => new { x.AreaTitle, x.AreaCode })
                  .Select(x => new TreeOutputDto<string>
                  {
                      Id = $"NotApi_{x.Key.AreaCode}",
                      Label = x.Key.AreaTitle,
                      Children = x
                          .Where(y => x.Key.AreaCode == y.AreaCode)
                          .GroupBy(a => new { a.ControllerTitle, a.ControllerCode })
                          .Select(z => new TreeOutputDto<string>
                          {
                              Id = $"NotApi_{z.Key.ControllerCode}",
                              Label = z.Key.ControllerTitle,
                              Children = z.Where(c => c.ControllerCode == z.Key.ControllerCode)
                                  .Select(d => new TreeOutputDto<string>
                                  {
                                      Id = d.Id.ToString(),
                                      Label = $"{d.ActionTitle}[代码：{d.Route}]",
                                      IsChecked = false,
                                      Description = d.Description
                                  }).ToList()

                          }).ToList()
                  }).ToList();

            return treeOutputDto;
        }

        /// <summary>
        ///  设置权限
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task PutSetAsync(List<SetPermissionInputDto> input)
        {
            if (input == null || !input.Any())
                throw new UserFriendlyException("请设置菜单的操作列表");

            //查询数据表的资源权限数据
            var resourceWithPermissionIdList = await _sysPermissionRepository
                .Where(x => x.Id != default)
                .ToListAsync(x => x.Id);

            using var uow = _freeSql.CreateUnitOfWork();

            _sysPermissionRepository.UnitOfWork = uow;

            var permissionList = ObjectMapper.Map<List<SetPermissionInputDto>, List<SysPermission>>(input);

            //新增则是权限id为空的
            var insertPermissionList = permissionList
                .Where(x => x.Id == default)
                .ToList();

            //删除则是权限没有在数据表中的
            var deletePermissionIdList = resourceWithPermissionIdList.Except(permissionList.Where(x => x.Id != default).Select(y => y.Id));

            //新增
            await _sysPermissionRepository.InsertAsync(insertPermissionList);

            //删除
            await _sysPermissionRepository.DeleteAsync(x => deletePermissionIdList.Contains(x.Id));

            uow.Commit();
        }

        /// <summary>
        ///  获取资源和资源对应的接口列表
        /// </summary>
        /// <returns></returns>
        public async Task<PermissionWithApiTreeListOutputDto> GetApiListAsync(Guid resourceId, Guid permissionId)
        {
            //查询资源权限列表
            var permissionApiList = await _sysPermissionApiRepository.Where(x => x.PermissionId == permissionId)
                .ToListAsync(x => new PermissionApiOutputDto
                {
                    ApiId = x.ApiId,
                    PermissionId = x.PermissionId
                });

            var permissionWithApiTreeList = new PermissionWithApiTreeListOutputDto
            {
                PermissionApiIdList = permissionApiList,
                ApiList = await GetApiTreeListAsync()
            };

            //查询资源数据
            var resource = await _sysResourceRepository
                .Where(x => x.Id == resourceId)
                .Include(x => x.Parent.Parent.Parent)
                .ToOneAsync();

            var resourcePathList = new List<string>();

            if (!string.IsNullOrWhiteSpace(resource.Parent?.Parent?.Title))
                resourcePathList.Add(resource.Parent?.Parent?.Title);

            if (!string.IsNullOrWhiteSpace(resource.Parent?.Title))
                resourcePathList.Add(resource.Parent?.Title);

            if (!string.IsNullOrWhiteSpace(resource.Title))
                resourcePathList.Add(resource.Title);

            permissionWithApiTreeList.ResourceTitle = string.Join('/', resourcePathList);

            return permissionWithApiTreeList;
        }

        /// <summary>
        ///  设置权限对应接口
        /// </summary>
        /// <returns></returns>
        public async Task SetApiAsync(List<SetPermissionApiInputDto> input)
        {
            if (input == null)
                throw new UserFriendlyException("请选择需要保存的权限数据");

            //去重权限编码
            var permissionIdList = input
                .GroupBy(x => x.PermissionId)
                .Select(x => x.Key);

            //先删除所有的权限对应的API列表数据
            await _sysPermissionApiRepository.DeleteAsync(x => permissionIdList.Contains(x.PermissionId.Value));

            //新增接口权限列表
            var permissionApiList = input
                .Where(x => x.ApiId.HasValue)
                .Select(y => new SysPermissionApi
                {
                    Id = Guid.NewGuid(),
                    PermissionId = y.PermissionId,
                    ApiId = y.ApiId
                });

            await _sysPermissionApiRepository.InsertAsync(permissionApiList);
        }
    }
}